package com.awise.app.timer.activity;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.awise.R;
import com.awise.util.DensityUtil;
import com.awise.util.Utils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by lihaifeng on 2018/1/22.
 */

public class ChartView extends View {
    private static final int DEFAULT_COUNT = 5;
    private int width;
    private int height;
    private float step;
    private float xInterval;
    private String[] titles = {"100%", "75%", "50%", "25%", "0%"};
    private String[] times1 = {"00:00", "12:00", "24:00"};
    private String[] times2 = {"00:00", "04:00", "08:00", "12:00", "16:00", "20:00", "24:00"};
    private ArrayList<FrameData> frames;
    private int cWidth;
    private int cHeight;
    private int startX;
    private int startY;
    private float timeInterval;
    private float yInterval;
    private int brightnessCount = DEFAULT_COUNT;

    private Paint hozorialLinePaint;
    private Paint timeTextPaint;
    private Paint polyinePaint1;
    private Paint polyinePaint2;
    private Paint polyinePaint3;
    private Paint polyinePaint4;
    private Paint polyinePaint5;
    private Paint verticalLinePaint;
    private Paint topViewPaint;
    private Paint topTextPaint;

    private float currentX;
    private int currentEditFrameIndex = 1;

    private int[] showBrightnesses;

    private boolean topBrightnessShow;
    private boolean verticalLineShow;
    private int periodType;
    private int oneWidth;
    private int timeRectWidth;
    private int timeRectHeight;
    private int arrowWidth;
    private int currentTime;
    private List<CurrentXChangeedListener> list = new ArrayList<>();

    private static final String LOGCAT = "ChartView";

    public interface CurrentXChangeedListener {
        void onChageed(boolean b);
    }

    public void addCurrentXChangedListener(CurrentXChangeedListener currentXChangeedListener) {
        list.add(currentXChangeedListener);
    }

    public void removeCurrentXChangedListener(CurrentXChangeedListener currentXChangeedListener) {
        list.remove(currentXChangeedListener);
    }

    public ChartView(Context context) {
        super(context);
        init();
    }

    public ChartView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public void setTopBrightnessViewAndVisibility(boolean visibility) {
        this.topBrightnessShow = visibility;
    }

    public void setVerticalLineVisibility(boolean visibility) {
        this.verticalLineShow = visibility;
    }

    public void setPeriodType(int type) {
        periodType = type;
    }

    public void setBrightnessCount(int count) {
        if(count < 0 || count > 5) {
            this.brightnessCount = DEFAULT_COUNT;
        } else {
            this.brightnessCount = count;
        }
    }
    public void init() {

        hozorialLinePaint = new Paint();
        hozorialLinePaint.setStrokeWidth(DensityUtil.dip2px(getContext(), 1));
        hozorialLinePaint.setStyle(Paint.Style.STROKE);
        hozorialLinePaint.setAntiAlias(true);
        hozorialLinePaint.setColor(getResources().getColor(R.color.color_brightness1));
        hozorialLinePaint.setPathEffect(new DashPathEffect(new float[]{DensityUtil.dip2px(getContext(), 2), DensityUtil.dip2px(getContext(), 2)}, 0));

        timeTextPaint = new Paint();
        timeTextPaint.setTextSize(DensityUtil.dip2px(getContext(), 12));
        timeTextPaint.setAntiAlias(true);
        timeTextPaint.setColor(getResources().getColor(R.color.color_brightness2));
        timeTextPaint.setTextAlign(Paint.Align.CENTER);

        int polylineWidth = DensityUtil.dip2px(getContext(), 2);
        polyinePaint1 = new Paint();
        polyinePaint1.setStyle(Paint.Style.STROKE);
        polyinePaint1.setAntiAlias(true);
        polyinePaint1.setStrokeWidth(polylineWidth);
        polyinePaint1.setColor(getResources().getColor(R.color.color_brightness1));

        polyinePaint2 = new Paint();
        polyinePaint2.setStyle(Paint.Style.STROKE);
        polyinePaint2.setAntiAlias(true);
        polyinePaint2.setStrokeWidth(polylineWidth);
        polyinePaint2.setColor(getResources().getColor(R.color.color_brightness2));

        polyinePaint3 = new Paint();
        polyinePaint3.setStyle(Paint.Style.STROKE);
        polyinePaint3.setAntiAlias(true);
        polyinePaint3.setStrokeWidth(polylineWidth);
        polyinePaint3.setColor(getResources().getColor(R.color.color_brightness3));

        polyinePaint4 = new Paint();
        polyinePaint4.setStyle(Paint.Style.STROKE);
        polyinePaint4.setAntiAlias(true);
        polyinePaint4.setStrokeWidth(polylineWidth);
        polyinePaint4.setColor(getResources().getColor(R.color.color_brightness4));

        polyinePaint5 = new Paint();
        polyinePaint5.setStyle(Paint.Style.STROKE);
        polyinePaint5.setAntiAlias(true);
        polyinePaint5.setStrokeWidth(polylineWidth);
        polyinePaint5.setColor(getResources().getColor(R.color.color_brightness5));

        verticalLinePaint = new Paint();
        verticalLinePaint.setStrokeWidth(DensityUtil.dip2px(getContext(), 2));
        verticalLinePaint.setStyle(Paint.Style.STROKE);
        verticalLinePaint.setAntiAlias(true);
        verticalLinePaint.setColor(getResources().getColor(R.color.color_brightness1));

        topViewPaint = new Paint();
        topViewPaint.setStrokeWidth(DensityUtil.dip2px(getContext(), 1));
        topViewPaint.setStyle(Paint.Style.STROKE);
        topViewPaint.setAntiAlias(true);
        topViewPaint.setColor(getResources().getColor(R.color.color_light_green));

        topTextPaint = new Paint();
        topTextPaint.setTextAlign(Paint.Align.CENTER);
        topTextPaint.setColor(getResources().getColor(R.color.color_light_grey));
        topTextPaint.setTextSize(DensityUtil.dip2px(getContext(), 14));
        topTextPaint.setAntiAlias(true);
        Typeface typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
        topTextPaint.setTypeface(typeface);
        topTextPaint.setFakeBoldText(true);

        showBrightnesses = new int[brightnessCount];
        startX = DensityUtil.dip2px(getContext(), 50);
        startY = DensityUtil.dip2px(getContext(), 33);
        oneWidth = DensityUtil.dip2px(getContext(), 50);
        timeRectWidth = DensityUtil.dip2px(getContext(), 90);
        timeRectHeight = DensityUtil.dip2px(getContext(), 26);
        arrowWidth = DensityUtil.dip2px(getContext(), 25);
        Date date = new Date();
        currentTime = date.getHours() * 60;
    }

    /**
     * 判断该时间点是否存在一帧数据
     * @return
     */
    private boolean frameExitsList() {
        byte[] time = axis2Time();
        for(int i = 1; i < frames.size()-1; i++) {
            FrameData data = frames.get(i);
            byte[] tempTime = data.getTime();
            if(time[0] == tempTime[0] &&  time[1] == tempTime[1]) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth();
        height = getMeasuredHeight();

        cWidth = width - 2 * startX;
        cHeight = height - 2 * startY;
        step = cHeight / 4.0f;
        xInterval = cWidth / 1440.0f;
        timeInterval = cWidth / 6.0f;

        yInterval = cHeight / 100.0f;

        currentX = startX + xInterval * currentTime;

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        setLayerType(LAYER_TYPE_SOFTWARE, null);
        //计算折线与时间线的交点的亮度值
        caculateBrightness(currentX);

        // 如果当前时间点已存在数据则通知所有监听者
        boolean flag = frameExitsList();
        for (int i= 0; i < list.size(); i++) {
            list.get(i).onChageed(flag);
        }

        // 画顶部亮度值
        if (topBrightnessShow) {
            int rectFWidth = oneWidth * brightnessCount;
            int left = width / 2 - rectFWidth / 2;
            int right = width / 2 + rectFWidth / 2;
            int top = DensityUtil.dip2px(getContext(), 1);
            int bottom = DensityUtil.dip2px(getContext(), 26);
            RectF rectF = new RectF(left, top, right, bottom);
            canvas.drawRect(rectF, topViewPaint);

            int index = left;
            for (int i = 0; i < brightnessCount; i++) {
                canvas.drawText(showBrightnesses[i] + "%", index + oneWidth / 2, bottom / 2 + DensityUtil.dip2px(getContext(), 7), topTextPaint);
                index += oneWidth;
                canvas.drawLine(index, top, index, bottom, topViewPaint);
            }
        }

        float lineTop = startY;
        //  画纵坐标亮度值及横线
        for (int i = 0; i < 5; i++) {
            canvas.drawText(titles[i], startX / 2, lineTop + DensityUtil.dip2px(getContext(), 4), timeTextPaint);
            canvas.drawLine(startX, lineTop, startX + cWidth, lineTop, hozorialLinePaint);
            lineTop += step;
        }

        int textLeft = startX;
        int timeTextHeight = height - startY + DensityUtil.dip2px(getContext(), 16);
        // 画横坐标时间值
        String[] times;
        if (periodType == 1) {
            times = times2;
            timeInterval = cWidth / 6.0f;
        } else {
            times = times1;
            timeInterval = cWidth / 2.0f;
        }
        for (int i = 0; i < times.length; i++) {
            canvas.drawText(times[i], textLeft, timeTextHeight, timeTextPaint);
            textLeft += timeInterval;
        }

        // 画折线
        if (frames != null) {
            if (hasData()) {
                for (int i = 0; i < brightnessCount; i++) {
                    Path path = new Path();
                    for (int j = 0; j < frames.size(); j++) {
                        FrameData data = frames.get(j);
                        if (j == 0) {
                            path.moveTo(time2Axis(data.getTime()), brightness2Axis(data.getBrightnesses()[i]));
                        } else {
                            float x, y, preY;
                            x = time2Axis(data.getTime());
                            y = brightness2Axis(data.getBrightnesses()[i]);
                            if (data.getEffect() == 1 && j > 0) {
                                FrameData preData = frames.get(j-1);
                                preY = brightness2Axis(preData.getBrightnesses()[i]);
                                path.lineTo(x, preY);
                            }
                            path.lineTo(x, y);
                        }
                    }
                    switch (i) {
                        case 0:
                            canvas.drawPath(path, polyinePaint1);
                            break;
                        case 1:
                            canvas.drawPath(path, polyinePaint2);
                            break;
                        case 2:
                            canvas.drawPath(path, polyinePaint3);
                            break;
                        case 3:
                            canvas.drawPath(path, polyinePaint4);
                            break;
                        case 4:
                            canvas.drawPath(path, polyinePaint5);
                            break;
                    }

                }
            }
        }

        // 画垂直线及时间微调View
        if (verticalLineShow) {
            canvas.drawLine(currentX, startY, currentX, height - startY, verticalLinePaint);
            float rectLeft = currentX - timeRectWidth / 2;
            float rectTop = startY + cHeight;
            float rectRight = currentX + timeRectWidth / 2;
            float rectBottom = rectTop + timeRectHeight;
            canvas.drawRect(rectLeft, rectTop, rectRight, rectBottom, topViewPaint);
            Path path1 = new Path();
            path1.moveTo(rectLeft + arrowWidth / 3 * 2, rectTop + timeRectHeight / 4);
            path1.lineTo(rectLeft + arrowWidth / 3, rectTop + timeRectHeight / 2);
            path1.lineTo(rectLeft + arrowWidth / 3 * 2, rectTop + timeRectHeight / 4 * 3);
            canvas.drawPath(path1, polyinePaint5);
            canvas.drawLine(rectLeft + arrowWidth, rectTop, rectLeft + arrowWidth, rectBottom, topViewPaint);
            canvas.drawText(Utils.formatTime(axis2Time()), currentX, rectBottom - DensityUtil.dip2px(getContext(), 8), topTextPaint);
            canvas.drawLine(rectRight - arrowWidth, rectTop, rectRight - arrowWidth, rectBottom, topViewPaint);
            Path path2 = new Path();
            path2.moveTo(rectRight - arrowWidth / 3 * 2, rectTop + timeRectHeight / 4);
            path2.lineTo(rectRight - arrowWidth / 3, rectTop + timeRectHeight / 2);
            path2.lineTo(rectRight - arrowWidth / 3 * 2, rectTop + timeRectHeight / 4 * 3);
            canvas.drawPath(path2, polyinePaint5);
        }
    }

    public float getCurrentX() {
        return currentX;
    }

    public void initCurrentX() {
        this.currentX = startX - xInterval;
        currentTime = 0;
        invalidate();
    }

    public void updateCurrentX() {
        currentX += xInterval;
        Log.d(LOGCAT, currentX + "");
        invalidate();
    }

    public boolean handleActionDownEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        // 时间微调
        if(x >= currentX - timeRectWidth / 2 && x<= currentX + timeRectWidth / 2 && y >= startY + cHeight && y <= startY + cHeight + timeRectHeight) {
            if(x <= currentX - timeRectWidth / 2 + arrowWidth ) {
                //向左微调
                currentX -= xInterval;
                if(currentX < startX) {
                    currentX = startX;
                }
            } else if(x >= currentX + timeRectWidth / 2 - arrowWidth) {
                //向右微调
                currentX += xInterval;
                if(currentX > startX + cWidth) {
                    currentX = startX + cWidth;
                }
            }
            invalidate();
            return false;
        } else if(x >= startX && x <= startX + cWidth && y >= startY && y <= startY + cHeight){
            this.currentX = x;
            invalidate();
            return true;
        } else {
            return false;
        }
    }

    public boolean handleActionMoveEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        // 判断边界条件
        if (x < startX || x > startX + cWidth || y < startY || y > startY + cHeight + timeRectHeight) {
            return false;
        }
        this.currentX = x;
        invalidate();
        return true;
    }

    public ArrayList<FrameData> getFrames() {
        return frames;
    }

    public void setFrames(ArrayList<FrameData> frames) {
        this.frames = frames;
        invalidate();
    }

    /**
     * 时间转化为横坐标
     *
     * @param time
     * @return
     */
    public float time2Axis(byte[] time) {
        int total = time[0] * 60 + time[1];
        return startX + xInterval * total;
    }

    //亮度转换为纵坐标
    public float brightness2Axis(byte brightness) {
        return startY + cHeight - yInterval * brightness;
    }

    public byte[] axis2Time() {
        int hour = (int) ((currentX - startX) / xInterval / 60);
        int minute = (int) (((currentX - startX) / xInterval) % 60);
        byte[] time = new byte[2];
        time[0] = (byte)hour;
        time[1] = (byte)minute;
        return time;
    }

    /**
     * 计算两条直线的交点 y=(b-d)x/(a-c)+(ad-bc)/(a-c)
     *
     * @param
     */
    public void caculateBrightness(float x) {
        float firstX = -1, secondX = -1;
        byte[] firstYs = null, secondYs = null;
        if (frames != null) {
            for (int i = 0; i < frames.size(); i++) {
                byte[] times = frames.get(i).getTime();
                FrameData firstFrame = frames.get(i);
                FrameData secondFrame = null;
                if (i + 1 < frames.size()) {
                    secondFrame = frames.get(i + 1);
                }
                if (secondFrame != null && x >= time2Axis(firstFrame.getTime()) && x <= time2Axis(secondFrame.getTime())) {
                    firstX = time2Axis(firstFrame.getTime());
                    firstYs = firstFrame.getBrightnesses();
                    secondX = time2Axis(secondFrame.getTime());
                    secondYs = secondFrame.getBrightnesses();
                    break;
                }
            }

            if (firstX != -1 && secondX != -1 && firstYs != null && secondYs != null) {
                for (int i = 0; i < brightnessCount; i++) {
                    float firstY = firstYs[i];
                    float secondY = secondYs[i];
                    showBrightnesses[i] = Math.round(((firstY - secondY) * x + (firstX * secondY - firstY * secondX)) / (firstX - secondX));
                }
            }
        }
    }

    public int[] getShowBrightnesses() {
        return this.showBrightnesses;
    }

    public void addFrame(FrameData frameData) {
        byte[] time = frameData.getTime();
        int index = -1;
        for (int i = 0; i < frames.size(); i++) {
            byte[] time1 = frames.get(i).getTime();
            byte[] time2 = null;
            if (i + 1 < frames.size()) {
                time2 = frames.get(i + 1).getTime();
            }
            if (time2 != null && (time1[0] * 60 + time1[1]) <= (time[0] * 60 + time[1])
                    && (time[0] * 60 + time[1]) <= (time2[0] * 60 + time2[1])) {
                index = i + 1;
            }
        }
        if(index != -1) {
            frames.add(index, frameData);
        }
    }

    public void removeFrame() {
        if (frames != null && frames.size() > 2) {
            frames.remove(frames.size() - 2);
            invalidate();
        }
    }

    public void deleteAllFrames() {
        frames.clear();
        FrameData firstData = new FrameData();
        firstData.setTime(new byte[]{0x00, 0x00});
        byte[] brightnesses = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
        firstData.setBrightnesses(brightnesses);
        firstData.setEffect((byte) 0x00);
        frames.add(firstData);

        FrameData lastData = new FrameData();
        lastData.setTime(new byte[]{0x18, 0x00});
        byte[] brightnesses4 = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
        lastData.setBrightnesses(brightnesses4);
        lastData.setEffect((byte) 0x00);
        frames.add(lastData);

        invalidate();

    }

    public void toPreviousFrame() {
        if (currentEditFrameIndex != -1 && frames != null && frames.size() > 2) {
            if (currentEditFrameIndex - 1 > 0) {
                currentEditFrameIndex--;
            } else {
                currentEditFrameIndex = frames.size() - 2;
            }
            currentX = time2Axis(frames.get(currentEditFrameIndex).getTime());
            invalidate();
        }

    }

    public void toNextFrame() {
        if (currentEditFrameIndex != -1 && frames != null && frames.size() > 2) {
            if (currentEditFrameIndex + 1 < frames.size() - 1) {
                currentEditFrameIndex++;
            } else {
                currentEditFrameIndex = 1;
            }
            currentX = time2Axis(frames.get(currentEditFrameIndex).getTime());
            invalidate();
        }

    }

    public boolean hasData() {
        boolean hasData = false;
        for (int i = 0; i < frames.size(); i++) {
            FrameData frameData = frames.get(i);
            byte[] brightness = frameData.getBrightnesses();
            for (int j = 0; j < brightness.length; j++) {
                if (brightness[j] != 0) {
                    hasData = true;
                }
            }
        }
        return hasData;
    }

}



